/*-------------------------------------------------------------------------
 *
 * date.c
 *	  实现 SQL 标准中规定的 DATE 和 TIME 数据类型
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/date.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include <ctype.h>
#include <limits.h>
#include <float.h>
#include <math.h>
#include <time.h>

#include "access/xact.h"
#include "catalog/pg_type.h"
#include "common/hashfn.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "nodes/supportnodes.h"
#include "parser/scansup.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datetime.h"
#include "utils/numeric.h"
#include "utils/sortsupport.h"

/*
 * gcc 的 -ffast-math 选项会破坏期望从
 * 表达式如 timeval / SECS_PER_HOUR 中获得精确结果的例程，其中 timeval 为 double。
 */
#ifdef __FAST_MATH__
#error -ffast-math is known to break this code
#endif


/* timetypmodin 和 timetztypmodin 的通用代码 */
static int32 fc_anytime_typmodin(bool fc_istz, ArrayType *fc_ta)
{
	int32	   *fc_tl;
	int			fc_n;

	fc_tl = ArrayGetIntegerTypmods(fc_ta, &fc_n);

	/*
	 * 我们对错误消息的质量并不太紧张，因为语法
	 * 不应该允许 TIME 的修饰符错误数量
	 */
	if (fc_n != 1)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid type modifier")));

	return anytime_typmod_check(fc_istz, fc_tl[0]);
}

/* 以便 parse_expr.c 能够使用它 */
int32 anytime_typmod_check(bool fc_istz, int32 fc_typmod)
{
	if (fc_typmod < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("TIME(%d)%s precision must not be negative",
						fc_typmod, (fc_istz ? " WITH TIME ZONE" : ""))));
	if (fc_typmod > MAX_TIME_PRECISION)
	{
		ereport(WARNING,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("TIME(%d)%s precision reduced to maximum allowed, %d",
						fc_typmod, (fc_istz ? " WITH TIME ZONE" : ""),
						MAX_TIME_PRECISION)));
		fc_typmod = MAX_TIME_PRECISION;
	}

	return fc_typmod;
}

/* timetypmodout和timetztypmodout的公共代码 */
static char * fc_anytime_typmodout(bool fc_istz, int32 fc_typmod)
{
	const char *fc_tz = fc_istz ? " with time zone" : " without time zone";

	if (fc_typmod >= 0)
		return psprintf("(%d)%s", (int) fc_typmod, fc_tz);
	else
		return psprintf("%s", fc_tz);
}


/*****************************************************************************
 *	 日期 ADT
 *****************************************************************************/


/* date_in()
 * 给定日期文本字符串，转换为内部日期格式。
 */
Datum date_in(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);
	DateADT		fc_date;
	fsec_t		fc_fsec;
	struct pg_tm fc_tt,
			   *fc_tm = &fc_tt;
	int			fc_tzp;
	int			fc_dtype;
	int			fc_nf;
	int			fc_dterr;
	char	   *fc_field[MAXDATEFIELDS];
	int			fc_ftype[MAXDATEFIELDS];
	char		fc_workbuf[MAXDATELEN + 1];

	fc_dterr = ParseDateTime(fc_str, fc_workbuf, sizeof(fc_workbuf),
						  fc_field, fc_ftype, MAXDATEFIELDS, &fc_nf);
	if (fc_dterr == 0)
		fc_dterr = DecodeDateTime(fc_field, fc_ftype, fc_nf, &fc_dtype, fc_tm, &fc_fsec, &fc_tzp);
	if (fc_dterr != 0)
		DateTimeParseError(fc_dterr, fc_str, "date");

	switch (fc_dtype)
	{
		case DTK_DATE:
			break;

		case DTK_EPOCH:
			GetEpochTime(fc_tm);
			break;

		case DTK_LATE:
			DATE_NOEND(fc_date);
			PG_RETURN_DATEADT(fc_date);

		case DTK_EARLY:
			DATE_NOBEGIN(fc_date);
			PG_RETURN_DATEADT(fc_date);

		default:
			DateTimeParseError(DTERR_BAD_FORMAT, fc_str, "date");
			break;
	}

	/* 防止 Julian 日 routines 溢出 */
	if (!IS_VALID_JULIAN(fc_tm->tm_year, fc_tm->tm_mon, tm->tm_mday))
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("date out of range: \"%s\"", fc_str)));

	fc_date = date2j(fc_tm->tm_year, fc_tm->tm_mon, fc_tm->tm_mday) - POSTGRES_EPOCH_JDATE;

	/* 现在检查超出范围的日期 */
	if (!IS_VALID_DATE(fc_date))
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("date out of range: \"%s\"", fc_str)));

	PG_RETURN_DATEADT(fc_date);
}

/* date_out()
 * 给定内部格式日期，转换为文本字符串。
 */
Datum date_out(PG_FUNCTION_ARGS)
{
	DateADT		fc_date = PG_GETARG_DATEADT(0);
	char	   *fc_result;
	struct pg_tm fc_tt,
			   *fc_tm = &fc_tt;
	char		fc_buf[MAXDATELEN + 1];

	if (DATE_NOT_FINITE(fc_date))
		EncodeSpecialDate(fc_date, fc_buf);
	else
	{
		j2date(fc_date + POSTGRES_EPOCH_JDATE,
			   &(fc_tm->tm_year), &(fc_tm->tm_mon), &(fc_tm->tm_mday));
		EncodeDateOnly(fc_tm, DateStyle, fc_buf);
	}

	fc_result = pstrdup(fc_buf);
	PG_RETURN_CSTRING(fc_result);
}

/*
 * date_recv - 将外部二进制格式转换为日期
 */
Datum date_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	DateADT		fc_result;

	fc_result = (DateADT) pq_getmsgint(fc_buf, sizeof(DateADT));

	/* 限制为date_in()接受的相同范围。 */
	if (DATE_NOT_FINITE(fc_result))
		 /* ok */ ;
	else if (!IS_VALID_DATE(fc_result))
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("date out of range")));

	PG_RETURN_DATEADT(fc_result);
}

/*
 *		date_send			-将日期转换为二进制格式
 */
Datum date_send(PG_FUNCTION_ARGS)
{
	DateADT		fc_date = PG_GETARG_DATEADT(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendint32(&fc_buf, fc_date);
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

/*
 *		make_date			- 日期构造函数
 */
Datum make_date(PG_FUNCTION_ARGS)
{
	struct pg_tm fc_tm;
	DateADT		fc_date;
	int			fc_dterr;
	bool		fc_bc = false;

	fc_tm.tm_year = PG_GETARG_INT32(0);
	fc_tm.tm_mon = PG_GETARG_INT32(1);
	fc_tm.tm_mday = PG_GETARG_INT32(2);

	/* 处理负年份作为公元前 */
	if (fc_tm.tm_year < 0)
	{
		fc_bc = true;
		fc_tm.tm_year = -fc_tm.tm_year;
	}

	fc_dterr = ValidateDate(DTK_DATE_M, false, false, fc_bc, &fc_tm);

	if (fc_dterr != 0)
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
				 errmsg("date field value out of range: %d-%02d-%02d",
						fc_tm.tm_year, fc_tm.tm_mon, fc_tm.tm_mday)));

	/* 防止 Julian 日 routines 溢出 */
	if (!IS_VALID_JULIAN(fc_tm.tm_year, fc_tm.tm_mon, tm.tm_mday))
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("date out of range: %d-%02d-%02d",
						fc_tm.tm_year, fc_tm.tm_mon, fc_tm.tm_mday)));

	fc_date = date2j(fc_tm.tm_year, fc_tm.tm_mon, fc_tm.tm_mday) - POSTGRES_EPOCH_JDATE;

	/* 现在检查超出范围的日期 */
	if (!IS_VALID_DATE(fc_date))
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("date out of range: %d-%02d-%02d",
						fc_tm.tm_year, fc_tm.tm_mon, fc_tm.tm_mday)));

	PG_RETURN_DATEADT(fc_date);
}

/*
 * 将保留的日期值转换为字符串。
 */
void EncodeSpecialDate(DateADT fc_dt, char *fc_str)
{
	if (DATE_IS_NOBEGIN(fc_dt))
		strcpy(fc_str, EARLY);
	else if (DATE_IS_NOEND(fc_dt))
		strcpy(fc_str, LATE);
	else						/* 不应该发生 */
		elog(ERROR, "invalid argument for EncodeSpecialDate");
}


 /*
 * GetSQLCurrentDate -- 实现 CURRENT_DATE
 */
DateADT
GetSQLCurrentDate(void)
{
	struct pg_tm fc_tm;

	static int	fc_cache_year = 0;
	static int	fc_cache_mon = 0;
	static int	fc_cache_mday = 0;
	static DateADT fc_cache_date;

	GetCurrentDateTime(&fc_tm);

	/*
	 * date2j 涉及几个整数除法；此外，除非我们的会话
	 * 跨越本地午夜，否则我们不需要多于
	 * 一次。因此在这里拥有一个单独的缓存似乎是值得的。
	 */  
	if (fc_tm.tm_year != fc_cache_year ||
		fc_tm.tm_mon != fc_cache_mon ||
		fc_tm.tm_mday != fc_cache_mday)
	{
		fc_cache_date = date2j(fc_tm.tm_year, fc_tm.tm_mon, fc_tm.tm_mday) - POSTGRES_EPOCH_JDATE;
		fc_cache_year = fc_tm.tm_year;
		fc_cache_mon = fc_tm.tm_mon;
		fc_cache_mday = fc_tm.tm_mday;
	}

	return fc_cache_date;
}

/*
 * GetSQLCurrentTime -- 实现 CURRENT_TIME, CURRENT_TIME(n)
 */
TimeTzADT *
GetSQLCurrentTime(int32 fc_typmod)
{
	TimeTzADT  *fc_result;
	struct pg_tm fc_tt,
			   *fc_tm = &fc_tt;
	fsec_t		fc_fsec;
	int			fc_tz;

	GetCurrentTimeUsec(fc_tm, &fc_fsec, &fc_tz);

	fc_result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
	tm2timetz(fc_tm, fc_fsec, fc_tz, fc_result);
	AdjustTimeForTypmod(&(fc_result->time), fc_typmod);
	return fc_result;
}

/*
 * GetSQLLocalTime -- 实现 LOCALTIME, LOCALTIME(n)
 */
TimeADT
GetSQLLocalTime(int32 fc_typmod)
{
	TimeADT		fc_result;
	struct pg_tm fc_tt,
			   *fc_tm = &fc_tt;
	fsec_t		fc_fsec;
	int			fc_tz;

	GetCurrentTimeUsec(fc_tm, &fc_fsec, &fc_tz);

	tm2time(fc_tm, fc_fsec, &fc_result);
	AdjustTimeForTypmod(&fc_result, fc_typmod);
	return fc_result;
}


/*
 * 日期的比较函数
 */

Datum date_eq(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		fc_dateVal2 = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(fc_dateVal1 == fc_dateVal2);
}

Datum date_ne(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		fc_dateVal2 = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(fc_dateVal1 != fc_dateVal2);
}

Datum date_lt(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		fc_dateVal2 = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(fc_dateVal1 < fc_dateVal2);
}

Datum date_le(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		fc_dateVal2 = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(fc_dateVal1 <= fc_dateVal2);
}

Datum date_gt(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		fc_dateVal2 = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(fc_dateVal1 > fc_dateVal2);
}

Datum date_ge(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		fc_dateVal2 = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(fc_dateVal1 >= fc_dateVal2);
}

Datum date_cmp(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		fc_dateVal2 = PG_GETARG_DATEADT(1);

	if (fc_dateVal1 < fc_dateVal2)
		PG_RETURN_INT32(-1);
	else if (fc_dateVal1 > fc_dateVal2)
		PG_RETURN_INT32(1);
	PG_RETURN_INT32(0);
}

Datum date_sortsupport(PG_FUNCTION_ARGS)
{
	SortSupport fc_ssup = (SortSupport) PG_GETARG_POINTER(0);

	fc_ssup->comparator = ssup_datum_int32_cmp;
	PG_RETURN_VOID();
}

Datum date_finite(PG_FUNCTION_ARGS)
{
	DateADT		fc_date = PG_GETARG_DATEADT(0);

	PG_RETURN_BOOL(!DATE_NOT_FINITE(fc_date));
}

Datum date_larger(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		fc_dateVal2 = PG_GETARG_DATEADT(1);

	PG_RETURN_DATEADT((fc_dateVal1 > fc_dateVal2) ? fc_dateVal1 : fc_dateVal2);
}

Datum date_smaller(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		fc_dateVal2 = PG_GETARG_DATEADT(1);

	PG_RETURN_DATEADT((fc_dateVal1 < fc_dateVal2) ? fc_dateVal1 : fc_dateVal2);
}

/* 计算两个日期之间的天数差异。
 */
Datum date_mi(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		fc_dateVal2 = PG_GETARG_DATEADT(1);

	if (DATE_NOT_FINITE(fc_dateVal1) || DATE_NOT_FINITE(fc_dateVal2))
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("cannot subtract infinite dates")));

	PG_RETURN_INT32((int32) (fc_dateVal1 - fc_dateVal2));
}

/* 将天数添加到日期，生成新的日期。
 * 必须处理正负天数。
 */
Datum date_pli(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	int32		fc_days = PG_GETARG_INT32(1);
	DateADT		fc_result;

	if (DATE_NOT_FINITE(fc_dateVal))
		PG_RETURN_DATEADT(fc_dateVal); /* 无法更改无穷大 */

	fc_result = fc_dateVal + fc_days;

	/* 检查整数溢出和超出允许范围 */
	if ((fc_days >= 0 ? (fc_result < fc_dateVal) : (fc_result > fc_dateVal)) ||
		!IS_VALID_DATE(fc_result))
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("date out of range")));

	PG_RETURN_DATEADT(fc_result);
}

/* 从日期中减去一定天数，得到新日期。 */
Datum date_mii(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	int32		fc_days = PG_GETARG_INT32(1);
	DateADT		fc_result;

	if (DATE_NOT_FINITE(fc_dateVal))
		PG_RETURN_DATEADT(fc_dateVal); /* 无法更改无穷大 */

	fc_result = fc_dateVal - fc_days;

	/* 检查整数溢出和超出允许范围 */
	if ((fc_days >= 0 ? (fc_result > fc_dateVal) : (fc_result < fc_dateVal)) ||
		!IS_VALID_DATE(fc_result))
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("date out of range")));

	PG_RETURN_DATEADT(fc_result);
}


/*
 * 将日期提升为时间戳。
 *
 * 成功转换后，如果 overflow 不是 NULL，则设置为零。
 *
 * 如果日期是有限的但超出时间戳的有效范围，那么：
 * 如果 overflow 是 NULL，我们抛出超出范围错误。
 * 如果 overflow 不是 NULL，我们在其中存储 +1 或 -1 以表明
 * 溢出的符号，并返回相应的时间戳无限值。
 *
 * 注意：*overflow = -1 当前实际上是不可能的，因为这两种
 * 数据类型具有相同的下界，即儒略日零。
 */
Timestamp date2timestamp_opt_overflow(DateADT fc_dateVal, int *fc_overflow)
{
	Timestamp	fc_result;

	if (fc_overflow)
		*fc_overflow = 0;

	if (DATE_IS_NOBEGIN(fc_dateVal))
		TIMESTAMP_NOBEGIN(fc_result);
	else if (DATE_IS_NOEND(fc_dateVal))
		TIMESTAMP_NOEND(fc_result);
	else
	{
		/*
		 * 由于日期的最小值与时间戳相同，只需检查上限
		 * 边界以防溢出。
		 */
		if (fc_dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
		{
			if (fc_overflow)
			{
				*fc_overflow = 1;
				TIMESTAMP_NOEND(fc_result);
				return fc_result;
			}
			else
			{
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("date out of range for timestamp")));
			}
		}

		/* 日期是自2000年以来的天数，时间戳是自同一以来的微秒... */
		fc_result = fc_dateVal * USECS_PER_DAY;
	}

	return fc_result;
}

/*
 * 将日期提升为时间戳，溢出时抛出错误。
 */
static TimestampTz fc_date2timestamp(DateADT fc_dateVal)
{
	return date2timestamp_opt_overflow(fc_dateVal, NULL);
}

/*
 * 将日期提升到带时区的时间戳。
 *
 * 在成功转换时，如果 overflow 不为 NULL，则设置为零。
 *
 * 如果日期是有限的，但超出了 timestamptz 的有效范围，则：
 * 如果 overflow 为 NULL，我们抛出超出范围的错误。
 * 如果 overflow 不为 NULL，我们在其中存储 +1 或 -1 以指示溢出的符号，
 * 并返回相应的 timestamptz 无穷大。
 */
TimestampTz date2timestamptz_opt_overflow(DateADT fc_dateVal, int *fc_overflow)
{
	TimestampTz fc_result;
	struct pg_tm fc_tt,
			   *fc_tm = &fc_tt;
	int			fc_tz;

	if (fc_overflow)
		*fc_overflow = 0;

	if (DATE_IS_NOBEGIN(fc_dateVal))
		TIMESTAMP_NOBEGIN(fc_result);
	else if (DATE_IS_NOEND(fc_dateVal))
		TIMESTAMP_NOEND(fc_result);
	else
	{
		/*
		 * 由于日期的最小值与时间戳相同，只需检查上限
		 * 边界以防溢出。
		 */
		if (fc_dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
		{
			if (fc_overflow)
			{
				*fc_overflow = 1;
				TIMESTAMP_NOEND(fc_result);
				return fc_result;
			}
			else
			{
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("date out of range for timestamp")));
			}
		}

		j2date(fc_dateVal + POSTGRES_EPOCH_JDATE,
			   &(fc_tm->tm_year), &(fc_tm->tm_mon), &(fc_tm->tm_mday));
		fc_tm->tm_hour = 0;
		fc_tm->tm_min = 0;
		fc_tm->tm_sec = 0;
		fc_tz = DetermineTimeZoneOffset(fc_tm, session_timezone);

		fc_result = fc_dateVal * USECS_PER_DAY + fc_tz * USECS_PER_SEC;

		/*
		 * 由于可能因为时区而超出允许的 timestamptz 范围，
		 * 因此在添加时区后检查允许的时间戳范围。
		 */
		if (!IS_VALID_TIMESTAMP(fc_result))
		{
			if (fc_overflow)
			{
				if (fc_result < MIN_TIMESTAMP)
				{
					*fc_overflow = -1;
					TIMESTAMP_NOBEGIN(fc_result);
				}
				else
				{
					*fc_overflow = 1;
					TIMESTAMP_NOEND(fc_result);
				}
			}
			else
			{
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("date out of range for timestamp")));
			}
		}
	}

	return fc_result;
}

/*
 * 将日期提升到 timestamptz，抛出溢出错误。
 */
static TimestampTz fc_date2timestamptz(DateADT fc_dateVal)
{
	return date2timestamptz_opt_overflow(fc_dateVal, NULL);
}

/*
 * date2timestamp_no_overflow
 *
 * 这是为了产生一个双精度值，该值在数值上等于相应的 Timestamp
 * 值，如果日期在 Timestamps 的有效范围内，但无论如何都不应抛出溢出错误。
 * 我们可以这样做，因为双精度数的数值范围大于
 * 无错误的时间戳。当前的结果仅用于统计估算目的。
 */
double date2timestamp_no_overflow(DateADT fc_dateVal)
{
	double		fc_result;

	if (DATE_IS_NOBEGIN(fc_dateVal))
		fc_result = -DBL_MAX;
	else if (DATE_IS_NOEND(fc_dateVal))
		fc_result = DBL_MAX;
	else
	{
		/* 日期是自2000年以来的天数，时间戳是自同一以来的微秒... */
		fc_result = fc_dateVal * (double) USECS_PER_DAY;
	}

	return fc_result;
}


/*
 * 日期的跨类型比较函数
 */

int32 date_cmp_timestamp_internal(DateADT fc_dateVal, Timestamp fc_dt2)
{
	Timestamp	fc_dt1;
	int			fc_overflow;

	fc_dt1 = date2timestamp_opt_overflow(fc_dateVal, &fc_overflow);
	if (fc_overflow > 0)
	{
		/* dt1 大于任何有限时间戳，但小于无穷大 */
		return TIMESTAMP_IS_NOEND(fc_dt2) ? -1 : +1;
	}
	Assert(fc_overflow == 0);		/* -1 的情况不能发生 */

	return timestamp_cmp_internal(fc_dt1, fc_dt2);
}

Datum date_eq_timestamp(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	Timestamp	fc_dt2 = PG_GETARG_TIMESTAMP(1);

	PG_RETURN_BOOL(date_cmp_timestamp_internal(fc_dateVal, fc_dt2) == 0);
}

Datum date_ne_timestamp(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	Timestamp	fc_dt2 = PG_GETARG_TIMESTAMP(1);

	PG_RETURN_BOOL(date_cmp_timestamp_internal(fc_dateVal, fc_dt2) != 0);
}

Datum date_lt_timestamp(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	Timestamp	fc_dt2 = PG_GETARG_TIMESTAMP(1);

	PG_RETURN_BOOL(date_cmp_timestamp_internal(fc_dateVal, fc_dt2) < 0);
}

Datum date_gt_timestamp(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	Timestamp	fc_dt2 = PG_GETARG_TIMESTAMP(1);

	PG_RETURN_BOOL(date_cmp_timestamp_internal(fc_dateVal, fc_dt2) > 0);
}

Datum date_le_timestamp(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	Timestamp	fc_dt2 = PG_GETARG_TIMESTAMP(1);

	PG_RETURN_BOOL(date_cmp_timestamp_internal(fc_dateVal, fc_dt2) <= 0);
}

Datum date_ge_timestamp(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	Timestamp	fc_dt2 = PG_GETARG_TIMESTAMP(1);

	PG_RETURN_BOOL(date_cmp_timestamp_internal(fc_dateVal, fc_dt2) >= 0);
}

Datum date_cmp_timestamp(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	Timestamp	fc_dt2 = PG_GETARG_TIMESTAMP(1);

	PG_RETURN_INT32(date_cmp_timestamp_internal(fc_dateVal, fc_dt2));
}

int32 date_cmp_timestamptz_internal(DateADT fc_dateVal, TimestampTz fc_dt2)
{
	TimestampTz fc_dt1;
	int			fc_overflow;

	fc_dt1 = date2timestamptz_opt_overflow(fc_dateVal, &fc_overflow);
	if (fc_overflow > 0)
	{
		/* dt1 大于任何有限时间戳，但小于无穷大 */
		return TIMESTAMP_IS_NOEND(fc_dt2) ? -1 : +1;
	}
	if (fc_overflow < 0)
	{
		/* dt1 小于任何有限时间戳，但大于 -infinity */
		return TIMESTAMP_IS_NOBEGIN(fc_dt2) ? +1 : -1;
	}

	return timestamptz_cmp_internal(fc_dt1, fc_dt2);
}

Datum date_eq_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	TimestampTz fc_dt2 = PG_GETARG_TIMESTAMPTZ(1);

	PG_RETURN_BOOL(date_cmp_timestamptz_internal(fc_dateVal, fc_dt2) == 0);
}

Datum date_ne_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	TimestampTz fc_dt2 = PG_GETARG_TIMESTAMPTZ(1);

	PG_RETURN_BOOL(date_cmp_timestamptz_internal(fc_dateVal, fc_dt2) != 0);
}

Datum date_lt_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	TimestampTz fc_dt2 = PG_GETARG_TIMESTAMPTZ(1);

	PG_RETURN_BOOL(date_cmp_timestamptz_internal(fc_dateVal, fc_dt2) < 0);
}

Datum date_gt_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	TimestampTz fc_dt2 = PG_GETARG_TIMESTAMPTZ(1);

	PG_RETURN_BOOL(date_cmp_timestamptz_internal(fc_dateVal, fc_dt2) > 0);
}

Datum date_le_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	TimestampTz fc_dt2 = PG_GETARG_TIMESTAMPTZ(1);

	PG_RETURN_BOOL(date_cmp_timestamptz_internal(fc_dateVal, fc_dt2) <= 0);
}

Datum date_ge_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	TimestampTz fc_dt2 = PG_GETARG_TIMESTAMPTZ(1);

	PG_RETURN_BOOL(date_cmp_timestamptz_internal(fc_dateVal, fc_dt2) >= 0);
}

Datum date_cmp_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	TimestampTz fc_dt2 = PG_GETARG_TIMESTAMPTZ(1);

	PG_RETURN_INT32(date_cmp_timestamptz_internal(fc_dateVal, fc_dt2));
}

Datum timestamp_eq_date(PG_FUNCTION_ARGS)
{
	Timestamp	fc_dt1 = PG_GETARG_TIMESTAMP(0);
	DateADT		fc_dateVal = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(date_cmp_timestamp_internal(fc_dateVal, fc_dt1) == 0);
}

Datum timestamp_ne_date(PG_FUNCTION_ARGS)
{
	Timestamp	fc_dt1 = PG_GETARG_TIMESTAMP(0);
	DateADT		fc_dateVal = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(date_cmp_timestamp_internal(fc_dateVal, fc_dt1) != 0);
}

Datum timestamp_lt_date(PG_FUNCTION_ARGS)
{
	Timestamp	fc_dt1 = PG_GETARG_TIMESTAMP(0);
	DateADT		fc_dateVal = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(date_cmp_timestamp_internal(fc_dateVal, fc_dt1) > 0);
}

Datum timestamp_gt_date(PG_FUNCTION_ARGS)
{
	Timestamp	fc_dt1 = PG_GETARG_TIMESTAMP(0);
	DateADT		fc_dateVal = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(date_cmp_timestamp_internal(fc_dateVal, fc_dt1) < 0);
}

Datum timestamp_le_date(PG_FUNCTION_ARGS)
{
	Timestamp	fc_dt1 = PG_GETARG_TIMESTAMP(0);
	DateADT		fc_dateVal = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(date_cmp_timestamp_internal(fc_dateVal, fc_dt1) >= 0);
}

Datum timestamp_ge_date(PG_FUNCTION_ARGS)
{
	Timestamp	fc_dt1 = PG_GETARG_TIMESTAMP(0);
	DateADT		fc_dateVal = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(date_cmp_timestamp_internal(fc_dateVal, fc_dt1) <= 0);
}

Datum timestamp_cmp_date(PG_FUNCTION_ARGS)
{
	Timestamp	fc_dt1 = PG_GETARG_TIMESTAMP(0);
	DateADT		fc_dateVal = PG_GETARG_DATEADT(1);

	PG_RETURN_INT32(-date_cmp_timestamp_internal(fc_dateVal, fc_dt1));
}

Datum timestamptz_eq_date(PG_FUNCTION_ARGS)
{
	TimestampTz fc_dt1 = PG_GETARG_TIMESTAMPTZ(0);
	DateADT		fc_dateVal = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(date_cmp_timestamptz_internal(fc_dateVal, fc_dt1) == 0);
}

Datum timestamptz_ne_date(PG_FUNCTION_ARGS)
{
	TimestampTz fc_dt1 = PG_GETARG_TIMESTAMPTZ(0);
	DateADT		fc_dateVal = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(date_cmp_timestamptz_internal(fc_dateVal, fc_dt1) != 0);
}

Datum timestamptz_lt_date(PG_FUNCTION_ARGS)
{
	TimestampTz fc_dt1 = PG_GETARG_TIMESTAMPTZ(0);
	DateADT		fc_dateVal = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(date_cmp_timestamptz_internal(fc_dateVal, fc_dt1) > 0);
}

Datum timestamptz_gt_date(PG_FUNCTION_ARGS)
{
	TimestampTz fc_dt1 = PG_GETARG_TIMESTAMPTZ(0);
	DateADT		fc_dateVal = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(date_cmp_timestamptz_internal(fc_dateVal, fc_dt1) < 0);
}

Datum timestamptz_le_date(PG_FUNCTION_ARGS)
{
	TimestampTz fc_dt1 = PG_GETARG_TIMESTAMPTZ(0);
	DateADT		fc_dateVal = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(date_cmp_timestamptz_internal(fc_dateVal, fc_dt1) >= 0);
}

Datum timestamptz_ge_date(PG_FUNCTION_ARGS)
{
	TimestampTz fc_dt1 = PG_GETARG_TIMESTAMPTZ(0);
	DateADT		fc_dateVal = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(date_cmp_timestamptz_internal(fc_dateVal, fc_dt1) <= 0);
}

Datum timestamptz_cmp_date(PG_FUNCTION_ARGS)
{
	TimestampTz fc_dt1 = PG_GETARG_TIMESTAMPTZ(0);
	DateADT		fc_dateVal = PG_GETARG_DATEADT(1);

	PG_RETURN_INT32(-date_cmp_timestamptz_internal(fc_dateVal, fc_dt1));
}

/*
 * 日期的 in_range 支持函数。
 *
 * 我们通过将日期提升为时间戳（无时区）来实现这一点，
 * 然后使用 timestamp-and-interval 的 in_range 函数。
 */
Datum in_range_date_interval(PG_FUNCTION_ARGS)
{
	DateADT		fc_val = PG_GETARG_DATEADT(0);
	DateADT		fc_base = PG_GETARG_DATEADT(1);
	Interval   *fc_offset = PG_GETARG_INTERVAL_P(2);
	bool		fc_sub = PG_GETARG_BOOL(3);
	bool		fc_less = PG_GETARG_BOOL(4);
	Timestamp	fc_valStamp;
	Timestamp	fc_baseStamp;

	/* XXX 我们也许可以在这里支持超范围情况 */
	fc_valStamp = fc_date2timestamp(fc_val);
	fc_baseStamp = fc_date2timestamp(fc_base);

	return DirectFunctionCall5(in_range_timestamp_interval,
							   TimestampGetDatum(fc_valStamp),
							   TimestampGetDatum(fc_baseStamp),
							   IntervalPGetDatum(fc_offset),
							   BoolGetDatum(fc_sub),
							   BoolGetDatum(fc_less));
}


/* extract_date()
 * 从日期类型中提取指定字段。
 */
Datum extract_date(PG_FUNCTION_ARGS)
{
	text	   *fc_units = PG_GETARG_TEXT_PP(0);
	DateADT		fc_date = PG_GETARG_DATEADT(1);
	int64		fc_intresult;
	int			fc_type,
				fc_val;
	char	   *fc_lowunits;
	int			fc_year,
				fc_mon,
				fc_mday;

	fc_lowunits = downcase_truncate_identifier(VARDATA_ANY(fc_units),
											VARSIZE_ANY_EXHDR(fc_units),
											false);

	fc_type = DecodeUnits(0, fc_lowunits, &fc_val);
	if (fc_type == UNKNOWN_FIELD)
		fc_type = DecodeSpecial(0, fc_lowunits, &fc_val);

	if (DATE_NOT_FINITE(fc_date) && (fc_type == UNITS || fc_type == RESERV))
	{
		switch (fc_val)
		{
				/* 振荡单位 */
			case DTK_DAY:
			case DTK_MONTH:
			case DTK_QUARTER:
			case DTK_WEEK:
			case DTK_DOW:
			case DTK_ISODOW:
			case DTK_DOY:
				PG_RETURN_NULL();
				break;

				/* 单调递增单位 */
			case DTK_YEAR:
			case DTK_DECADE:
			case DTK_CENTURY:
			case DTK_MILLENNIUM:
			case DTK_JULIAN:
			case DTK_ISOYEAR:
			case DTK_EPOCH:
				if (DATE_IS_NOBEGIN(fc_date))
					PG_RETURN_NUMERIC(DatumGetNumeric(DirectFunctionCall3(numeric_in,
																		  CStringGetDatum("-Infinity"),
																		  ObjectIdGetDatum(InvalidOid),
																		  Int32GetDatum(-1))));
				else
					PG_RETURN_NUMERIC(DatumGetNumeric(DirectFunctionCall3(numeric_in,
																		  CStringGetDatum("Infinity"),
																		  ObjectIdGetDatum(InvalidOid),
																		  Int32GetDatum(-1))));
			default:
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("unit \"%s\" not supported for type %s",
								fc_lowunits, format_type_be(DATEOID))));
		}
	}
	else if (fc_type == UNITS)
	{
		j2date(fc_date + POSTGRES_EPOCH_JDATE, &fc_year, &fc_mon, &fc_mday);

		switch (fc_val)
		{
			case DTK_DAY:
				fc_intresult = fc_mday;
				break;

			case DTK_MONTH:
				fc_intresult = fc_mon;
				break;

			case DTK_QUARTER:
				fc_intresult = (fc_mon - 1) / 3 + 1;
				break;

			case DTK_WEEK:
				fc_intresult = date2isoweek(fc_year, fc_mon, fc_mday);
				break;

			case DTK_YEAR:
				if (fc_year > 0)
					fc_intresult = fc_year;
				else
					/* 没有年份0，只有1 BC和1 AD */
					fc_intresult = fc_year - 1;
				break;

			case DTK_DECADE:
				/* 请参阅timestamp_part中的注释 */
				if (fc_year >= 0)
					fc_intresult = fc_year / 10;
				else
					fc_intresult = -((8 - (fc_year - 1)) / 10);
				break;

			case DTK_CENTURY:
				/* 请参阅timestamp_part中的注释 */
				if (fc_year > 0)
					fc_intresult = (fc_year + 99) / 100;
				else
					fc_intresult = -((99 - (fc_year - 1)) / 100);
				break;

			case DTK_MILLENNIUM:
				/* 请参阅timestamp_part中的注释 */
				if (fc_year > 0)
					fc_intresult = (fc_year + 999) / 1000;
				else
					fc_intresult = -((999 - (fc_year - 1)) / 1000);
				break;

			case DTK_JULIAN:
				fc_intresult = fc_date + POSTGRES_EPOCH_JDATE;
				break;

			case DTK_ISOYEAR:
				fc_intresult = date2isoyear(fc_year, fc_mon, fc_mday);
				/* 调整 BC 年 */
				if (fc_intresult <= 0)
					fc_intresult -= 1;
				break;

			case DTK_DOW:
			case DTK_ISODOW:
				fc_intresult = j2day(fc_date + POSTGRES_EPOCH_JDATE);
				if (fc_val == DTK_ISODOW && fc_intresult == 0)
					fc_intresult = 7;
				break;

			case DTK_DOY:
				fc_intresult = date2j(fc_year, fc_mon, fc_mday) - date2j(fc_year, 1, 1) + 1;
				break;

			default:
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("unit \"%s\" not supported for type %s",
								fc_lowunits, format_type_be(DATEOID))));
				fc_intresult = 0;
		}
	}
	else if (fc_type == RESERV)
	{
		switch (fc_val)
		{
			case DTK_EPOCH:
				fc_intresult = ((int64) fc_date + POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY;
				break;

			default:
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("unit \"%s\" not supported for type %s",
								fc_lowunits, format_type_be(DATEOID))));
				fc_intresult = 0;
		}
	}
	else
	{
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("unit \"%s\" not recognized for type %s",
						fc_lowunits, format_type_be(DATEOID))));
		fc_intresult = 0;
	}

	PG_RETURN_NUMERIC(int64_to_numeric(fc_intresult));
}


/* 向日期添加一个间隔，生成一个新日期。
 * 必须处理正负间隔。
 *
 * 我们通过将日期提升为时间戳（不带时区）
 * 然后使用时间戳加上间隔函数来实现这一点。
 */
Datum date_pl_interval(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	Interval   *fc_span = PG_GETARG_INTERVAL_P(1);
	Timestamp	fc_dateStamp;

	fc_dateStamp = fc_date2timestamp(fc_dateVal);

	return DirectFunctionCall2(timestamp_pl_interval,
							   TimestampGetDatum(fc_dateStamp),
							   PointerGetDatum(fc_span));
}

/* 从日期中减去一个时间间隔，给出一个新日期。
 * 必须处理正负时间间隔。
 *
 * 我们通过将日期提升为时间戳（没有时区）来实现
 * 然后使用时间戳减去时间间隔的函数。
 */
Datum date_mi_interval(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	Interval   *fc_span = PG_GETARG_INTERVAL_P(1);
	Timestamp	fc_dateStamp;

	fc_dateStamp = fc_date2timestamp(fc_dateVal);

	return DirectFunctionCall2(timestamp_mi_interval,
							   TimestampGetDatum(fc_dateStamp),
							   PointerGetDatum(fc_span));
}

/* date_timestamp()
 * 将日期转换为时间戳数据类型。
 */
Datum date_timestamp(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	Timestamp	fc_result;

	fc_result = fc_date2timestamp(fc_dateVal);

	PG_RETURN_TIMESTAMP(fc_result);
}

/* timestamp_date()
 * 将时间戳转换为日期数据类型。
 */
Datum timestamp_date(PG_FUNCTION_ARGS)
{
	Timestamp	fc_timestamp = PG_GETARG_TIMESTAMP(0);
	DateADT		fc_result;
	struct pg_tm fc_tt,
			   *fc_tm = &fc_tt;
	fsec_t		fc_fsec;

	if (TIMESTAMP_IS_NOBEGIN(fc_timestamp))
		DATE_NOBEGIN(fc_result);
	else if (TIMESTAMP_IS_NOEND(fc_timestamp))
		DATE_NOEND(fc_result);
	else
	{
		if (timestamp2tm(fc_timestamp, NULL, fc_tm, &fc_fsec, NULL, NULL) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

		fc_result = date2j(fc_tm->tm_year, fc_tm->tm_mon, fc_tm->tm_mday) - POSTGRES_EPOCH_JDATE;
	}

	PG_RETURN_DATEADT(fc_result);
}


/* date_timestamptz()
 * 将日期转换为带时区的数据类型的时间戳。
 */
Datum date_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		fc_dateVal = PG_GETARG_DATEADT(0);
	TimestampTz fc_result;

	fc_result = fc_date2timestamptz(fc_dateVal);

	PG_RETURN_TIMESTAMP(fc_result);
}


/* timestamptz_date()
 * 将带时区的时间戳转换为日期数据类型。
 */
Datum timestamptz_date(PG_FUNCTION_ARGS)
{
	TimestampTz fc_timestamp = PG_GETARG_TIMESTAMP(0);
	DateADT		fc_result;
	struct pg_tm fc_tt,
			   *fc_tm = &fc_tt;
	fsec_t		fc_fsec;
	int			fc_tz;

	if (TIMESTAMP_IS_NOBEGIN(fc_timestamp))
		DATE_NOBEGIN(fc_result);
	else if (TIMESTAMP_IS_NOEND(fc_timestamp))
		DATE_NOEND(fc_result);
	else
	{
		if (timestamp2tm(fc_timestamp, &fc_tz, fc_tm, &fc_fsec, NULL, NULL) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

		fc_result = date2j(fc_tm->tm_year, fc_tm->tm_mon, fc_tm->tm_mday) - POSTGRES_EPOCH_JDATE;
	}

	PG_RETURN_DATEADT(fc_result);
}


/*****************************************************************************
 *	 时间抽象数据类型
 *****************************************************************************/

Datum time_in(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);

#ifdef NOT_USED
	Oid			fc_typelem = PG_GETARG_OID(1);
#endif
	int32		fc_typmod = PG_GETARG_INT32(2);
	TimeADT		fc_result;
	fsec_t		fc_fsec;
	struct pg_tm fc_tt,
			   *fc_tm = &fc_tt;
	int			fc_tz;
	int			fc_nf;
	int			fc_dterr;
	char		fc_workbuf[MAXDATELEN + 1];
	char	   *fc_field[MAXDATEFIELDS];
	int			fc_dtype;
	int			fc_ftype[MAXDATEFIELDS];

	fc_dterr = ParseDateTime(fc_str, fc_workbuf, sizeof(fc_workbuf),
						  fc_field, fc_ftype, MAXDATEFIELDS, &fc_nf);
	if (fc_dterr == 0)
		fc_dterr = DecodeTimeOnly(fc_field, fc_ftype, fc_nf, &fc_dtype, fc_tm, &fc_fsec, &fc_tz);
	if (fc_dterr != 0)
		DateTimeParseError(fc_dterr, fc_str, "time");

	tm2time(fc_tm, fc_fsec, &fc_result);
	AdjustTimeForTypmod(&fc_result, fc_typmod);

	PG_RETURN_TIMEADT(fc_result);
}

/* tm2time()
 * 将 tm 结构转换为时间数据类型。
 */
int tm2time(struct pg_tm *fc_tm, fsec_t fc_fsec, TimeADT *fc_result)
{
	*fc_result = ((((fc_tm->tm_hour * MINS_PER_HOUR + fc_tm->tm_min) * SECS_PER_MINUTE) + fc_tm->tm_sec)
			   * USECS_PER_SEC) + fc_fsec;
	return 0;
}

/* time_overflows()
 * 检查分解后的时间是否超出范围。
 */
bool time_overflows(int fc_hour, int fc_min, int fc_sec, fsec_t fc_fsec)
{
	/* 单独对字段进行范围检查。 */
	if (fc_hour < 0 || fc_hour > HOURS_PER_DAY ||
		fc_min < 0 || fc_min >= MINS_PER_HOUR ||
		fc_sec < 0 || fc_sec > SECS_PER_MINUTE ||
		fc_fsec < 0 || fc_fsec > USECS_PER_SEC)
		return true;

	/*
	 * 因为我们允许，例如，hour = 24 或 sec = 60，所以我们必须单独检查总时间值是否超过 24:00:00。
	 */
	if ((((((fc_hour * MINS_PER_HOUR + fc_min) * SECS_PER_MINUTE)
		   + fc_sec) * USECS_PER_SEC) + fc_fsec) > USECS_PER_DAY)
		return true;

	return false;
}

/* float_time_overflows()
 * 同样，当我们将秒 + 分秒作为一个 "双精度" 值时。
 */
bool float_time_overflows(int fc_hour, int fc_min, double fc_sec)
{
	/* 单独对字段进行范围检查。 */
	if (fc_hour < 0 || fc_hour > HOURS_PER_DAY ||
		fc_min < 0 || fc_min >= MINS_PER_HOUR)
		return true;

	/*
	 * “sec”因其双精度特性，需要额外小心。在应用范围检查之前处理 NaN，并进行舍入，以避免因不精确输入而导致的意外错误。（我们假设 rint() 在面对无穷大时表现正常。）
	 */
	if (isnan(fc_sec))
		return true;
	fc_sec = rint(fc_sec * USECS_PER_SEC);
	if (fc_sec < 0 || fc_sec > SECS_PER_MINUTE * USECS_PER_SEC)
		return true;

	/*
	 * 因为我们允许，例如，hour = 24 或 sec = 60，
	 * 我们必须单独检查总时间值是否超过 24:00:00。
	 * 这必须与调用者将字段转换为时间的方式匹配。
	 */
	if (((((fc_hour * MINS_PER_HOUR + fc_min) * SECS_PER_MINUTE)
		  * USECS_PER_SEC) + (int64) fc_sec) > USECS_PER_DAY)
		return true;

	return false;
}


/* time2tm()
 * 将时间数据类型转换为 POSIX 时间结构。
 *
 * 请注意，只有小时/分钟/秒/分数秒字段被填充。
 */
int time2tm(TimeADT fc_time, struct pg_tm *fc_tm, fsec_t *fc_fsec)
{
	fc_tm->tm_hour = fc_time / USECS_PER_HOUR;
	fc_time -= fc_tm->tm_hour * USECS_PER_HOUR;
	fc_tm->tm_min = fc_time / USECS_PER_MINUTE;
	fc_time -= fc_tm->tm_min * USECS_PER_MINUTE;
	fc_tm->tm_sec = fc_time / USECS_PER_SEC;
	fc_time -= fc_tm->tm_sec * USECS_PER_SEC;
	*fc_fsec = fc_time;
	return 0;
}

Datum time_out(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time = PG_GETARG_TIMEADT(0);
	char	   *fc_result;
	struct pg_tm fc_tt,
			   *fc_tm = &fc_tt;
	fsec_t		fc_fsec;
	char		fc_buf[MAXDATELEN + 1];

	time2tm(fc_time, fc_tm, &fc_fsec);
	EncodeTimeOnly(fc_tm, fc_fsec, false, 0, DateStyle, fc_buf);

	fc_result = pstrdup(fc_buf);
	PG_RETURN_CSTRING(fc_result);
}

/*
 *		time_recv			- 将外部二进制格式转换为时间
 */
Datum time_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);

#ifdef NOT_USED
	Oid			fc_typelem = PG_GETARG_OID(1);
#endif
	int32		fc_typmod = PG_GETARG_INT32(2);
	TimeADT		fc_result;

	fc_result = pq_getmsgint64(fc_buf);

	if (fc_result < INT64CONST(0) || fc_result > USECS_PER_DAY)
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("time out of range")));

	AdjustTimeForTypmod(&fc_result, fc_typmod);

	PG_RETURN_TIMEADT(fc_result);
}

/*
 *		time_send			- 将时间转换为二进制格式
 */
Datum time_send(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time = PG_GETARG_TIMEADT(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendint64(&fc_buf, fc_time);
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

Datum timetypmodin(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_ta = PG_GETARG_ARRAYTYPE_P(0);

	PG_RETURN_INT32(fc_anytime_typmodin(false, fc_ta));
}

Datum timetypmodout(PG_FUNCTION_ARGS)
{
	int32		fc_typmod = PG_GETARG_INT32(0);

	PG_RETURN_CSTRING(fc_anytime_typmodout(false, fc_typmod));
}

/*
 *		make_time			- 时间构造函数
 */
Datum make_time(PG_FUNCTION_ARGS)
{
	int			fc_tm_hour = PG_GETARG_INT32(0);
	int			fc_tm_min = PG_GETARG_INT32(1);
	double		fc_sec = PG_GETARG_FLOAT8(2);
	TimeADT		fc_time;

	/* 检查时间溢出 */
	if (float_time_overflows(fc_tm_hour, fc_tm_min, fc_sec))
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
				 errmsg("time field value out of range: %d:%02d:%02g",
						fc_tm_hour, fc_tm_min, fc_sec)));

	/* 这应该与 tm2time 匹配 */
	fc_time = (((fc_tm_hour * MINS_PER_HOUR + fc_tm_min) * SECS_PER_MINUTE)
			* USECS_PER_SEC) + (int64) rint(fc_sec * USECS_PER_SEC);

	PG_RETURN_TIMEADT(fc_time);
}


/* time_support()
 *
 * 规划支持函数，用于time_scale()和timetz_scale()
 * 长度强制函数（我们这里不需要区分它们）。
 */
Datum time_support(PG_FUNCTION_ARGS)
{
	Node	   *fc_rawreq = (Node *) PG_GETARG_POINTER(0);
	Node	   *fc_ret = NULL;

	if (IsA(fc_rawreq, SupportRequestSimplify))
	{
		SupportRequestSimplify *fc_req = (SupportRequestSimplify *) fc_rawreq;

		fc_ret = TemporalSimplify(MAX_TIME_PRECISION, (Node *) fc_req->fcall);
	}

	PG_RETURN_POINTER(fc_ret);
}

/* time_scale()
 * 根据指定的比例因子调整时间类型。
 * 被 PostgreSQL 类型系统用于填充列。
 */
Datum time_scale(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time = PG_GETARG_TIMEADT(0);
	int32		fc_typmod = PG_GETARG_INT32(1);
	TimeADT		fc_result;

	fc_result = fc_time;
	AdjustTimeForTypmod(&fc_result, fc_typmod);

	PG_RETURN_TIMEADT(fc_result);
}

/* AdjustTimeForTypmod()
 * 强制时间值的精度为指定值。
 * 使用的 *完全* 与 AdjustTimestampForTypmod() 中的代码相同
 * 但我们做了一个单独的副本，因为这些类型并没有
 * 基本联系，而是实现上的巧合。 - thomas
 */
void AdjustTimeForTypmod(TimeADT *fc_time, int32 fc_typmod)
{
	static const int64 fc_TimeScales[MAX_TIME_PRECISION + 1] = {
		INT64CONST(1000000),
		INT64CONST(100000),
		INT64CONST(10000),
		INT64CONST(1000),
		INT64CONST(100),
		INT64CONST(10),
		INT64CONST(1)
	};

	static const int64 fc_TimeOffsets[MAX_TIME_PRECISION + 1] = {
		INT64CONST(500000),
		INT64CONST(50000),
		INT64CONST(5000),
		INT64CONST(500),
		INT64CONST(50),
		INT64CONST(5),
		INT64CONST(0)
	};

	if (fc_typmod >= 0 && fc_typmod <= MAX_TIME_PRECISION)
	{
		if (*fc_time >= INT64CONST(0))
			*fc_time = ((*fc_time + fc_TimeOffsets[fc_typmod]) / fc_TimeScales[fc_typmod]) *
				fc_TimeScales[fc_typmod];
		else
			*fc_time = -((((-*fc_time) + fc_TimeOffsets[fc_typmod]) / fc_TimeScales[fc_typmod]) *
					  fc_TimeScales[fc_typmod]);
	}
}


Datum time_eq(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time1 = PG_GETARG_TIMEADT(0);
	TimeADT		fc_time2 = PG_GETARG_TIMEADT(1);

	PG_RETURN_BOOL(fc_time1 == fc_time2);
}

Datum time_ne(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time1 = PG_GETARG_TIMEADT(0);
	TimeADT		fc_time2 = PG_GETARG_TIMEADT(1);

	PG_RETURN_BOOL(fc_time1 != fc_time2);
}

Datum time_lt(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time1 = PG_GETARG_TIMEADT(0);
	TimeADT		fc_time2 = PG_GETARG_TIMEADT(1);

	PG_RETURN_BOOL(fc_time1 < fc_time2);
}

Datum time_le(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time1 = PG_GETARG_TIMEADT(0);
	TimeADT		fc_time2 = PG_GETARG_TIMEADT(1);

	PG_RETURN_BOOL(fc_time1 <= fc_time2);
}

Datum time_gt(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time1 = PG_GETARG_TIMEADT(0);
	TimeADT		fc_time2 = PG_GETARG_TIMEADT(1);

	PG_RETURN_BOOL(fc_time1 > fc_time2);
}

Datum time_ge(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time1 = PG_GETARG_TIMEADT(0);
	TimeADT		fc_time2 = PG_GETARG_TIMEADT(1);

	PG_RETURN_BOOL(fc_time1 >= fc_time2);
}

Datum time_cmp(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time1 = PG_GETARG_TIMEADT(0);
	TimeADT		fc_time2 = PG_GETARG_TIMEADT(1);

	if (fc_time1 < fc_time2)
		PG_RETURN_INT32(-1);
	if (fc_time1 > fc_time2)
		PG_RETURN_INT32(1);
	PG_RETURN_INT32(0);
}

Datum time_hash(PG_FUNCTION_ARGS)
{
	return hashint8(fcinfo);
}

Datum time_hash_extended(PG_FUNCTION_ARGS)
{
	return hashint8extended(fcinfo);
}

Datum time_larger(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time1 = PG_GETARG_TIMEADT(0);
	TimeADT		fc_time2 = PG_GETARG_TIMEADT(1);

	PG_RETURN_TIMEADT((fc_time1 > fc_time2) ? fc_time1 : fc_time2);
}

Datum time_smaller(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time1 = PG_GETARG_TIMEADT(0);
	TimeADT		fc_time2 = PG_GETARG_TIMEADT(1);

	PG_RETURN_TIMEADT((fc_time1 < fc_time2) ? fc_time1 : fc_time2);
}

/* overlaps_time() --- 实现 SQL OVERLAPS 运算符。
 *
 * 算法根据 SQL 规范。 这比你想象的要困难得多，
 * 因为规范要求我们在某些情况下提供非空答案，
 * 即便某些输入为空。
 */
Datum overlaps_time(PG_FUNCTION_ARGS)
{
	/*
	 * 参数是 TimeADT，但我们将它们保留为通用 Datum，以避免解引用空值（TimeADT 是按引用传递的！）
	 */
	Datum		fc_ts1 = PG_GETARG_DATUM(0);
	Datum		fc_te1 = PG_GETARG_DATUM(1);
	Datum		fc_ts2 = PG_GETARG_DATUM(2);
	Datum		fc_te2 = PG_GETARG_DATUM(3);
	bool		fc_ts1IsNull = PG_ARGISNULL(0);
	bool		fc_te1IsNull = PG_ARGISNULL(1);
	bool		fc_ts2IsNull = PG_ARGISNULL(2);
	bool		fc_te2IsNull = PG_ARGISNULL(3);

#define TIMEADT_GT(t1,t2) \
	(DatumGetTimeADT(t1) > DatumGetTimeADT(t2))
#define TIMEADT_LT(t1,t2) \
	(DatumGetTimeADT(t1) < DatumGetTimeADT(t2))

	/*
	 * 如果区间1的两个端点都是null，结果为null（未知）。
	 * 如果只有一个端点为null，则取ts1为非null值。 否则，取ts1为较小的端点。
	 */
	if (fc_ts1IsNull)
	{
		if (fc_te1IsNull)
			PG_RETURN_NULL();
		/* 用非空值交换空值 */
		fc_ts1 = fc_te1;
		fc_te1IsNull = true;
	}
	else if (!fc_te1IsNull)
	{
		if (TIMEADT_GT(fc_ts1, fc_te1))
		{
			Datum		fc_tt = fc_ts1;

			fc_ts1 = fc_te1;
			fc_te1 = fc_tt;
		}
	}

	/* 对于区间 2 也是如此。 */
	if (fc_ts2IsNull)
	{
		if (fc_te2IsNull)
			PG_RETURN_NULL();
		/* 用非空值交换空值 */
		fc_ts2 = fc_te2;
		fc_te2IsNull = true;
	}
	else if (!fc_te2IsNull)
	{
		if (TIMEADT_GT(fc_ts2, fc_te2))
		{
			Datum		fc_tt = fc_ts2;

			fc_ts2 = fc_te2;
			fc_te2 = fc_tt;
		}
	}

	/*
	 * 此时，ts1 和 ts2 均不为空，因此我们可以考虑三种情况：
	 * ts1 > ts2，ts1 < ts2，ts1 = ts2
	 */
	if (TIMEADT_GT(fc_ts1, fc_ts2))
	{
		/*
		 * 这个情况是ts1 < te2 OR te1 < te2，这看起来可能冗余，
		 * 但在存在空值时并不完全如此。
		 */
		if (fc_te2IsNull)
			PG_RETURN_NULL();
		if (TIMEADT_LT(fc_ts1, fc_te2))
			PG_RETURN_BOOL(true);
		if (fc_te1IsNull)
			PG_RETURN_NULL();

		/*
		 * 如果te1不为空，那么我们之前有ts1 <= te1，并且我们刚刚发现ts1 >= te2，
		 * 因此te1 >= te2。
		 */
		PG_RETURN_BOOL(false);
	}
	else if (TIMEADT_LT(fc_ts1, fc_ts2))
	{
		/* 这种情况是 ts2 < te1 或 te2 < te1 */
		if (fc_te1IsNull)
			PG_RETURN_NULL();
		if (TIMEADT_LT(fc_ts2, fc_te1))
			PG_RETURN_BOOL(true);
		if (fc_te2IsNull)
			PG_RETURN_NULL();

		/*
		 * 如果te2不为null，则我们以上有ts2 <= te2，并且我们刚刚发现ts2 >= te1，因此te2 >= te1。
		 */
		PG_RETURN_BOOL(false);
	}
	else
	{
		/*
		 * 对于 ts1 = ts2，规范说 te1 <> te2 或 te1 = te2，这是一种
		 * 相当愚蠢的方式来表达“如果两者都不是 NULL 则为真，否则为 NULL”。
		 */
		if (fc_te1IsNull || fc_te2IsNull)
			PG_RETURN_NULL();
		PG_RETURN_BOOL(true);
	}

#undef TIMEADT_GT
#undef TIMEADT_LT
}

/* timestamp_time()
 * 将时间戳转换为时间数据类型。
 */
Datum timestamp_time(PG_FUNCTION_ARGS)
{
	Timestamp	fc_timestamp = PG_GETARG_TIMESTAMP(0);
	TimeADT		fc_result;
	struct pg_tm fc_tt,
			   *fc_tm = &fc_tt;
	fsec_t		fc_fsec;

	if (TIMESTAMP_NOT_FINITE(fc_timestamp))
		PG_RETURN_NULL();

	if (timestamp2tm(fc_timestamp, NULL, fc_tm, &fc_fsec, NULL, NULL) != 0)
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));

	/*
	 * 也可以这样做 time = (timestamp / USECS_PER_DAY *
	 * USECS_PER_DAY) - timestamp;
	 */
	fc_result = ((((fc_tm->tm_hour * MINS_PER_HOUR + fc_tm->tm_min) * SECS_PER_MINUTE) + fc_tm->tm_sec) *
			  USECS_PER_SEC) + fc_fsec;

	PG_RETURN_TIMEADT(fc_result);
}

/* timestamptz_time()
 * 将 timestamptz 转换为时间数据类型。
 */
Datum timestamptz_time(PG_FUNCTION_ARGS)
{
	TimestampTz fc_timestamp = PG_GETARG_TIMESTAMP(0);
	TimeADT		fc_result;
	struct pg_tm fc_tt,
			   *fc_tm = &fc_tt;
	int			fc_tz;
	fsec_t		fc_fsec;

	if (TIMESTAMP_NOT_FINITE(fc_timestamp))
		PG_RETURN_NULL();

	if (timestamp2tm(fc_timestamp, &fc_tz, fc_tm, &fc_fsec, NULL, NULL) != 0)
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));

	/*
	 * 也可以这样做 time = (timestamp / USECS_PER_DAY *
	 * USECS_PER_DAY) - timestamp;
	 */
	fc_result = ((((fc_tm->tm_hour * MINS_PER_HOUR + fc_tm->tm_min) * SECS_PER_MINUTE) + fc_tm->tm_sec) *
			  USECS_PER_SEC) + fc_fsec;

	PG_RETURN_TIMEADT(fc_result);
}

/* datetime_timestamp()
 * 将日期和时间转换为时间戳数据类型。
 */
Datum datetime_timestamp(PG_FUNCTION_ARGS)
{
	DateADT		fc_date = PG_GETARG_DATEADT(0);
	TimeADT		fc_time = PG_GETARG_TIMEADT(1);
	Timestamp	fc_result;

	fc_result = fc_date2timestamp(fc_date);
	if (!TIMESTAMP_NOT_FINITE(fc_result))
	{
		fc_result += fc_time;
		if (!IS_VALID_TIMESTAMP(fc_result))
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
	}

	PG_RETURN_TIMESTAMP(fc_result);
}

/* time_interval()
 * 将时间转换为间隔数据类型。
 */
Datum time_interval(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time = PG_GETARG_TIMEADT(0);
	Interval   *fc_result;

	fc_result = (Interval *) palloc(sizeof(Interval));

	fc_result->time = fc_time;
	fc_result->day = 0;
	fc_result->month = 0;

	PG_RETURN_INTERVAL_P(fc_result);
}

/* interval_time()
 * 将间隔转换为时间数据类型。
 *
 * 这被定义为生成间隔的分数天部分。
 * 因此，我们可以忽略月份字段。
 * 处理负间隔时并不十分清楚该怎么做，
 * 但是我们选择减去下限，因此，例如，
 * “-2小时”变为“22:00:00”。
 */
Datum interval_time(PG_FUNCTION_ARGS)
{
	Interval   *fc_span = PG_GETARG_INTERVAL_P(0);
	TimeADT		fc_result;
	int64		fc_days;

	fc_result = fc_span->time;
	if (fc_result >= USECS_PER_DAY)
	{
		fc_days = fc_result / USECS_PER_DAY;
		fc_result -= fc_days * USECS_PER_DAY;
	}
	else if (fc_result < 0)
	{
		fc_days = (-fc_result + USECS_PER_DAY - 1) / USECS_PER_DAY;
		fc_result += fc_days * USECS_PER_DAY;
	}

	PG_RETURN_TIMEADT(fc_result);
}

/* time_mi_time()
 * 减去两个时间以产生间隔。
 */
Datum time_mi_time(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time1 = PG_GETARG_TIMEADT(0);
	TimeADT		fc_time2 = PG_GETARG_TIMEADT(1);
	Interval   *fc_result;

	fc_result = (Interval *) palloc(sizeof(Interval));

	fc_result->month = 0;
	fc_result->day = 0;
	fc_result->time = fc_time1 - fc_time2;

	PG_RETURN_INTERVAL_P(fc_result);
}

/* time_pl_interval()
 * 将间隔加到时间上。
 */
Datum time_pl_interval(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time = PG_GETARG_TIMEADT(0);
	Interval   *fc_span = PG_GETARG_INTERVAL_P(1);
	TimeADT		fc_result;

	fc_result = fc_time + fc_span->time;
	fc_result -= fc_result / USECS_PER_DAY * USECS_PER_DAY;
	if (fc_result < INT64CONST(0))
		fc_result += USECS_PER_DAY;

	PG_RETURN_TIMEADT(fc_result);
}

/* time_mi_interval()
 * 从时间中减去间隔。
 */
Datum time_mi_interval(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time = PG_GETARG_TIMEADT(0);
	Interval   *fc_span = PG_GETARG_INTERVAL_P(1);
	TimeADT		fc_result;

	fc_result = fc_time - fc_span->time;
	fc_result -= fc_result / USECS_PER_DAY * USECS_PER_DAY;
	if (fc_result < INT64CONST(0))
		fc_result += USECS_PER_DAY;

	PG_RETURN_TIMEADT(fc_result);
}

/*
 * 时间的 in_range 支持函数。
 */
Datum in_range_time_interval(PG_FUNCTION_ARGS)
{
	TimeADT		fc_val = PG_GETARG_TIMEADT(0);
	TimeADT		fc_base = PG_GETARG_TIMEADT(1);
	Interval   *fc_offset = PG_GETARG_INTERVAL_P(2);
	bool		fc_sub = PG_GETARG_BOOL(3);
	bool		fc_less = PG_GETARG_BOOL(4);
	TimeADT		fc_sum;

	/*
	 * 类似于 time_pl_interval/time_mi_interval，
	 * 我们不考虑偏移的月份和天数字段。
	 * 所以我们对负数的测试也应该如此。
	 */
	if (fc_offset->time < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
				 errmsg("invalid preceding or following size in window function")));

	/*
	 * 我们不能在这里使用 time_pl_interval/time_mi_interval，因为它们的 
	 * 包装行为会给出错误（或至少不理想）的答案。 
	 * 幸运的是，等价的非包装行为很简单，尤其是 
	 * 因为我们不担心整数溢出。
	 */
	if (fc_sub)
		fc_sum = fc_base - fc_offset->time;
	else
		fc_sum = fc_base + fc_offset->time;

	if (fc_less)
		PG_RETURN_BOOL(fc_val <= fc_sum);
	else
		PG_RETURN_BOOL(fc_val >= fc_sum);
}


/* time_part() 和 extract_time()
 * 从时间类型提取指定字段。
 */
static Datum fc_time_part_common(PG_FUNCTION_ARGS, bool fc_retnumeric)
{
	text	   *fc_units = PG_GETARG_TEXT_PP(0);
	TimeADT		fc_time = PG_GETARG_TIMEADT(1);
	int64		fc_intresult;
	int			fc_type,
				fc_val;
	char	   *fc_lowunits;

	fc_lowunits = downcase_truncate_identifier(VARDATA_ANY(fc_units),
											VARSIZE_ANY_EXHDR(fc_units),
											false);

	fc_type = DecodeUnits(0, fc_lowunits, &fc_val);
	if (fc_type == UNKNOWN_FIELD)
		fc_type = DecodeSpecial(0, fc_lowunits, &fc_val);

	if (fc_type == UNITS)
	{
		fsec_t		fc_fsec;
		struct pg_tm fc_tt,
				   *fc_tm = &fc_tt;

		time2tm(fc_time, fc_tm, &fc_fsec);

		switch (fc_val)
		{
			case DTK_MICROSEC:
				fc_intresult = fc_tm->tm_sec * INT64CONST(1000000) + fc_fsec;
				break;

			case DTK_MILLISEC:
				if (fc_retnumeric)
					/*---
					 * tm->tm_sec * 1000 + fsec / 1000
					 * = (tm->tm_sec * 1'000'000 + fsec) / 1000
					 */
					PG_RETURN_NUMERIC(int64_div_fast_to_numeric(fc_tm->tm_sec * INT64CONST(1000000) + fc_fsec, 3));
				else
					PG_RETURN_FLOAT8(fc_tm->tm_sec * 1000.0 + fc_fsec / 1000.0);
				break;

			case DTK_SECOND:
				if (fc_retnumeric)
					/*---
					 * tm->tm_sec + fsec / 1'000'000
					 * = (tm->tm_sec * 1'000'000 + fsec) / 1'000'000
					 */
					PG_RETURN_NUMERIC(int64_div_fast_to_numeric(fc_tm->tm_sec * INT64CONST(1000000) + fc_fsec, 6));
				else
					PG_RETURN_FLOAT8(fc_tm->tm_sec + fc_fsec / 1000000.0);
				break;

			case DTK_MINUTE:
				fc_intresult = fc_tm->tm_min;
				break;

			case DTK_HOUR:
				fc_intresult = fc_tm->tm_hour;
				break;

			case DTK_TZ:
			case DTK_TZ_MINUTE:
			case DTK_TZ_HOUR:
			case DTK_DAY:
			case DTK_MONTH:
			case DTK_QUARTER:
			case DTK_YEAR:
			case DTK_DECADE:
			case DTK_CENTURY:
			case DTK_MILLENNIUM:
			case DTK_ISOYEAR:
			default:
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("unit \"%s\" not supported for type %s",
								fc_lowunits, format_type_be(TIMEOID))));
				fc_intresult = 0;
		}
	}
	else if (fc_type == RESERV && fc_val == DTK_EPOCH)
	{
		if (fc_retnumeric)
			PG_RETURN_NUMERIC(int64_div_fast_to_numeric(fc_time, 6));
		else
			PG_RETURN_FLOAT8(fc_time / 1000000.0);
	}
	else
	{
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("unit \"%s\" not recognized for type %s",
						fc_lowunits, format_type_be(TIMEOID))));
		fc_intresult = 0;
	}

	if (fc_retnumeric)
		PG_RETURN_NUMERIC(int64_to_numeric(fc_intresult));
	else
		PG_RETURN_FLOAT8(fc_intresult);
}

Datum time_part(PG_FUNCTION_ARGS)
{
	return fc_time_part_common(fcinfo, false);
}

Datum extract_time(PG_FUNCTION_ARGS)
{
	return fc_time_part_common(fcinfo, true);
}


/*****************************************************************************
 *	 带时区的时间 ADT
 *****************************************************************************/

/* tm2timetz()
 * 将 tm 结构转换为时间数据类型。
 */
int tm2timetz(struct pg_tm *fc_tm, fsec_t fc_fsec, int fc_tz, TimeTzADT *fc_result)
{
	fc_result->time = ((((fc_tm->tm_hour * MINS_PER_HOUR + fc_tm->tm_min) * SECS_PER_MINUTE) + fc_tm->tm_sec) *
					USECS_PER_SEC) + fc_fsec;
	fc_result->zone = fc_tz;

	return 0;
}

Datum timetz_in(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);

#ifdef NOT_USED
	Oid			fc_typelem = PG_GETARG_OID(1);
#endif
	int32		fc_typmod = PG_GETARG_INT32(2);
	TimeTzADT  *fc_result;
	fsec_t		fc_fsec;
	struct pg_tm fc_tt,
			   *fc_tm = &fc_tt;
	int			fc_tz;
	int			fc_nf;
	int			fc_dterr;
	char		fc_workbuf[MAXDATELEN + 1];
	char	   *fc_field[MAXDATEFIELDS];
	int			fc_dtype;
	int			fc_ftype[MAXDATEFIELDS];

	fc_dterr = ParseDateTime(fc_str, fc_workbuf, sizeof(fc_workbuf),
						  fc_field, fc_ftype, MAXDATEFIELDS, &fc_nf);
	if (fc_dterr == 0)
		fc_dterr = DecodeTimeOnly(fc_field, fc_ftype, fc_nf, &fc_dtype, fc_tm, &fc_fsec, &fc_tz);
	if (fc_dterr != 0)
		DateTimeParseError(fc_dterr, fc_str, "time with time zone");

	fc_result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
	tm2timetz(fc_tm, fc_fsec, fc_tz, fc_result);
	AdjustTimeForTypmod(&(fc_result->time), fc_typmod);

	PG_RETURN_TIMETZADT_P(fc_result);
}

Datum timetz_out(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_time = PG_GETARG_TIMETZADT_P(0);
	char	   *fc_result;
	struct pg_tm fc_tt,
			   *fc_tm = &fc_tt;
	fsec_t		fc_fsec;
	int			fc_tz;
	char		fc_buf[MAXDATELEN + 1];

	timetz2tm(fc_time, fc_tm, &fc_fsec, &fc_tz);
	EncodeTimeOnly(fc_tm, fc_fsec, true, fc_tz, DateStyle, fc_buf);

	fc_result = pstrdup(fc_buf);
	PG_RETURN_CSTRING(fc_result);
}

/*
 *		timetz_recv			- 将外部二进制格式转换为 timetz
 */
Datum timetz_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);

#ifdef NOT_USED
	Oid			fc_typelem = PG_GETARG_OID(1);
#endif
	int32		fc_typmod = PG_GETARG_INT32(2);
	TimeTzADT  *fc_result;

	fc_result = (TimeTzADT *) palloc(sizeof(TimeTzADT));

	fc_result->time = pq_getmsgint64(fc_buf);

	if (fc_result->time < INT64CONST(0) || fc_result->time > USECS_PER_DAY)
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("time out of range")));

	fc_result->zone = pq_getmsgint(fc_buf, sizeof(fc_result->zone));

	/* 检查合理的 GMT 位移；请参见 datatype/timestamp.h 中的备注 */
	if (fc_result->zone <= -TZDISP_LIMIT || fc_result->zone >= TZDISP_LIMIT)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
				 errmsg("time zone displacement out of range")));

	AdjustTimeForTypmod(&(fc_result->time), fc_typmod);

	PG_RETURN_TIMETZADT_P(fc_result);
}

/*
 *		timetz_send			- 将 timetz 转换为二进制格式
 */
Datum timetz_send(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_time = PG_GETARG_TIMETZADT_P(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendint64(&fc_buf, fc_time->time);
	pq_sendint32(&fc_buf, fc_time->zone);
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

Datum timetztypmodin(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_ta = PG_GETARG_ARRAYTYPE_P(0);

	PG_RETURN_INT32(fc_anytime_typmodin(true, fc_ta));
}

Datum timetztypmodout(PG_FUNCTION_ARGS)
{
	int32		fc_typmod = PG_GETARG_INT32(0);

	PG_RETURN_CSTRING(fc_anytime_typmodout(true, fc_typmod));
}


/* timetz2tm()
 * 将带时区的时间数据类型转换为 POSIX 时间结构。
 */
int timetz2tm(TimeTzADT *fc_time, struct pg_tm *fc_tm, fsec_t *fc_fsec, int *fc_tzp)
{
	TimeOffset	fc_trem = fc_time->time;

	fc_tm->tm_hour = fc_trem / USECS_PER_HOUR;
	fc_trem -= fc_tm->tm_hour * USECS_PER_HOUR;
	fc_tm->tm_min = fc_trem / USECS_PER_MINUTE;
	fc_trem -= fc_tm->tm_min * USECS_PER_MINUTE;
	fc_tm->tm_sec = fc_trem / USECS_PER_SEC;
	*fc_fsec = fc_trem - fc_tm->tm_sec * USECS_PER_SEC;

	if (fc_tzp != NULL)
		*fc_tzp = fc_time->zone;

	return 0;
}

/* timetz_scale()
 * 根据指定的比例因子调整时间类型。
 * 被 PostgreSQL 类型系统用于填充列。
 */
Datum timetz_scale(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_time = PG_GETARG_TIMETZADT_P(0);
	int32		fc_typmod = PG_GETARG_INT32(1);
	TimeTzADT  *fc_result;

	fc_result = (TimeTzADT *) palloc(sizeof(TimeTzADT));

	fc_result->time = fc_time->time;
	fc_result->zone = fc_time->zone;

	AdjustTimeForTypmod(&(fc_result->time), fc_typmod);

	PG_RETURN_TIMETZADT_P(fc_result);
}


static int fc_timetz_cmp_internal(TimeTzADT *fc_time1, TimeTzADT *fc_time2)
{
	TimeOffset	fc_t1,
				fc_t2;

	/* 主要排序是按真实（GMT 等效）时间 */
	fc_t1 = fc_time1->time + (fc_time1->zone * USECS_PER_SEC);
	fc_t2 = fc_time2->time + (fc_time2->zone * USECS_PER_SEC);

	if (fc_t1 > fc_t2)
		return 1;
	if (fc_t1 < fc_t2)
		return -1;

	/*
	 * 如果 GMT 时间相同，则按时区排序；我们只想在两个
	 * timetz 相等时，如果时间和时区部分都相等。
	 */
	if (fc_time1->zone > fc_time2->zone)
		return 1;
	if (fc_time1->zone < fc_time2->zone)
		return -1;

	return 0;
}

Datum timetz_eq(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *fc_time2 = PG_GETARG_TIMETZADT_P(1);

	PG_RETURN_BOOL(fc_timetz_cmp_internal(fc_time1, fc_time2) == 0);
}

Datum timetz_ne(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *fc_time2 = PG_GETARG_TIMETZADT_P(1);

	PG_RETURN_BOOL(fc_timetz_cmp_internal(fc_time1, fc_time2) != 0);
}

Datum timetz_lt(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *fc_time2 = PG_GETARG_TIMETZADT_P(1);

	PG_RETURN_BOOL(fc_timetz_cmp_internal(fc_time1, fc_time2) < 0);
}

Datum timetz_le(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *fc_time2 = PG_GETARG_TIMETZADT_P(1);

	PG_RETURN_BOOL(fc_timetz_cmp_internal(fc_time1, fc_time2) <= 0);
}

Datum timetz_gt(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *fc_time2 = PG_GETARG_TIMETZADT_P(1);

	PG_RETURN_BOOL(fc_timetz_cmp_internal(fc_time1, fc_time2) > 0);
}

Datum timetz_ge(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *fc_time2 = PG_GETARG_TIMETZADT_P(1);

	PG_RETURN_BOOL(fc_timetz_cmp_internal(fc_time1, fc_time2) >= 0);
}

Datum timetz_cmp(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *fc_time2 = PG_GETARG_TIMETZADT_P(1);

	PG_RETURN_INT32(fc_timetz_cmp_internal(fc_time1, fc_time2));
}

Datum timetz_hash(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_key = PG_GETARG_TIMETZADT_P(0);
	uint32		fc_thash;

	/* 
	 * 为避免结构中的填充字节问题，我们分别计算字段哈希值并进行异或。
	 */
	fc_thash = DatumGetUInt32(DirectFunctionCall1(hashint8,
											   Int64GetDatumFast(fc_key->time)));
	fc_thash ^= DatumGetUInt32(hash_uint32(fc_key->zone));
	PG_RETURN_UINT32(fc_thash);
}

Datum timetz_hash_extended(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_key = PG_GETARG_TIMETZADT_P(0);
	Datum		fc_seed = PG_GETARG_DATUM(1);
	uint64		fc_thash;

	/* 同样的方法作为 timetz_hash */
	fc_thash = DatumGetUInt64(DirectFunctionCall2(hashint8extended,
											   Int64GetDatumFast(fc_key->time),
											   fc_seed));
	fc_thash ^= DatumGetUInt64(hash_uint32_extended(fc_key->zone,
												 DatumGetInt64(fc_seed)));
	PG_RETURN_UINT64(fc_thash);
}

Datum timetz_larger(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *fc_time2 = PG_GETARG_TIMETZADT_P(1);
	TimeTzADT  *fc_result;

	if (fc_timetz_cmp_internal(fc_time1, fc_time2) > 0)
		fc_result = fc_time1;
	else
		fc_result = fc_time2;
	PG_RETURN_TIMETZADT_P(fc_result);
}

Datum timetz_smaller(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *fc_time2 = PG_GETARG_TIMETZADT_P(1);
	TimeTzADT  *fc_result;

	if (fc_timetz_cmp_internal(fc_time1, fc_time2) < 0)
		fc_result = fc_time1;
	else
		fc_result = fc_time2;
	PG_RETURN_TIMETZADT_P(fc_result);
}

/* timetz_pl_interval()
 * 将间隔添加到 timetz。
 */
Datum timetz_pl_interval(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_time = PG_GETARG_TIMETZADT_P(0);
	Interval   *fc_span = PG_GETARG_INTERVAL_P(1);
	TimeTzADT  *fc_result;

	fc_result = (TimeTzADT *) palloc(sizeof(TimeTzADT));

	fc_result->time = fc_time->time + fc_span->time;
	fc_result->time -= fc_result->time / USECS_PER_DAY * USECS_PER_DAY;
	if (fc_result->time < INT64CONST(0))
		fc_result->time += USECS_PER_DAY;

	fc_result->zone = fc_time->zone;

	PG_RETURN_TIMETZADT_P(fc_result);
}

/* timetz_mi_interval()
 * 从 timetz 中减去间隔。
 */
Datum timetz_mi_interval(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_time = PG_GETARG_TIMETZADT_P(0);
	Interval   *fc_span = PG_GETARG_INTERVAL_P(1);
	TimeTzADT  *fc_result;

	fc_result = (TimeTzADT *) palloc(sizeof(TimeTzADT));

	fc_result->time = fc_time->time - fc_span->time;
	fc_result->time -= fc_result->time / USECS_PER_DAY * USECS_PER_DAY;
	if (fc_result->time < INT64CONST(0))
		fc_result->time += USECS_PER_DAY;

	fc_result->zone = fc_time->zone;

	PG_RETURN_TIMETZADT_P(fc_result);
}

/* 
 * timetz 的 in_range 支持函数。
 */
Datum in_range_timetz_interval(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_val = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *fc_base = PG_GETARG_TIMETZADT_P(1);
	Interval   *fc_offset = PG_GETARG_INTERVAL_P(2);
	bool		fc_sub = PG_GETARG_BOOL(3);
	bool		fc_less = PG_GETARG_BOOL(4);
	TimeTzADT	fc_sum;

	/*
	 * 像 timetz_pl_interval/timetz_mi_interval 一样，我们不考虑偏移量的月份和日字段。因此我们对负数的测试也应如此。
	 */
	if (fc_offset->time < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
				 errmsg("invalid preceding or following size in window function")));

	/*
	 * 我们不能在这里使用 timetz_pl_interval/timetz_mi_interval，因为它们的
	 * 环绕行为会给出错误（或者至少不受欢迎）的答案。
	 * 幸运的是，等效的非环绕行为是微不足道的，尤其是
	 * 因为我们不担心整数溢出。
	 */
	if (fc_sub)
		fc_sum.time = fc_base->time - fc_offset->time;
	else
		fc_sum.time = fc_base->time + fc_offset->time;
	fc_sum.zone = fc_base->zone;

	if (fc_less)
		PG_RETURN_BOOL(fc_timetz_cmp_internal(fc_val, &fc_sum) <= 0);
	else
		PG_RETURN_BOOL(fc_timetz_cmp_internal(fc_val, &fc_sum) >= 0);
}

/* overlaps_timetz() --- 实现 SQL OVERLAPS 运算符。
 *
 * 算法遵循 SQL 规范。这比你想象的要困难得多，
 * 因为规范要求我们在一些输入为 null 的情况下，
 * 返回一个非空的答案。
 */
Datum overlaps_timetz(PG_FUNCTION_ARGS)
{
	/*
	 * 参数是 TimeTzADT *，但为了方便表示，我们将它们保留为通用
	 * Datum 类型 —— 并避免解引用 null。
	 */
	Datum		fc_ts1 = PG_GETARG_DATUM(0);
	Datum		fc_te1 = PG_GETARG_DATUM(1);
	Datum		fc_ts2 = PG_GETARG_DATUM(2);
	Datum		fc_te2 = PG_GETARG_DATUM(3);
	bool		fc_ts1IsNull = PG_ARGISNULL(0);
	bool		fc_te1IsNull = PG_ARGISNULL(1);
	bool		fc_ts2IsNull = PG_ARGISNULL(2);
	bool		fc_te2IsNull = PG_ARGISNULL(3);

#define TIMETZ_GT(t1,t2) \
	DatumGetBool(DirectFunctionCall2(timetz_gt,t1,t2))
#define TIMETZ_LT(t1,t2) \
	DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2))

	/*
	 * 如果区间1的两个端点都是null，结果为null（未知）。
	 * 如果只有一个端点为null，则取ts1为非null值。 否则，取ts1为较小的端点。
	 */
	if (fc_ts1IsNull)
	{
		if (fc_te1IsNull)
			PG_RETURN_NULL();
		/* 用非空值交换空值 */
		fc_ts1 = fc_te1;
		fc_te1IsNull = true;
	}
	else if (!fc_te1IsNull)
	{
		if (TIMETZ_GT(fc_ts1, fc_te1))
		{
			Datum		fc_tt = fc_ts1;

			fc_ts1 = fc_te1;
			fc_te1 = fc_tt;
		}
	}

	/* 对于区间 2 也是如此。 */
	if (fc_ts2IsNull)
	{
		if (fc_te2IsNull)
			PG_RETURN_NULL();
		/* 用非空值交换空值 */
		fc_ts2 = fc_te2;
		fc_te2IsNull = true;
	}
	else if (!fc_te2IsNull)
	{
		if (TIMETZ_GT(fc_ts2, fc_te2))
		{
			Datum		fc_tt = fc_ts2;

			fc_ts2 = fc_te2;
			fc_te2 = fc_tt;
		}
	}

	/*
	 * 此时，ts1 和 ts2 均不为空，因此我们可以考虑三种情况：
	 * ts1 > ts2，ts1 < ts2，ts1 = ts2
	 */
	if (TIMETZ_GT(fc_ts1, fc_ts2))
	{
		/*
		 * 这个情况是ts1 < te2 OR te1 < te2，这看起来可能冗余，
		 * 但在存在空值时并不完全如此。
		 */
		if (fc_te2IsNull)
			PG_RETURN_NULL();
		if (TIMETZ_LT(fc_ts1, fc_te2))
			PG_RETURN_BOOL(true);
		if (fc_te1IsNull)
			PG_RETURN_NULL();

		/*
		 * 如果te1不为空，那么我们之前有ts1 <= te1，并且我们刚刚发现ts1 >= te2，
		 * 因此te1 >= te2。
		 */
		PG_RETURN_BOOL(false);
	}
	else if (TIMETZ_LT(fc_ts1, fc_ts2))
	{
		/* 这种情况是 ts2 < te1 或 te2 < te1 */
		if (fc_te1IsNull)
			PG_RETURN_NULL();
		if (TIMETZ_LT(fc_ts2, fc_te1))
			PG_RETURN_BOOL(true);
		if (fc_te2IsNull)
			PG_RETURN_NULL();

		/*
		 * 如果te2不为null，则我们以上有ts2 <= te2，并且我们刚刚发现ts2 >= te1，因此te2 >= te1。
		 */
		PG_RETURN_BOOL(false);
	}
	else
	{
		/*
		 * 对于 ts1 = ts2，规范说 te1 <> te2 或 te1 = te2，这是一种
		 * 相当愚蠢的方式来表达“如果两者都不是 NULL 则为真，否则为 NULL”。
		 */
		if (fc_te1IsNull || fc_te2IsNull)
			PG_RETURN_NULL();
		PG_RETURN_BOOL(true);
	}

#undef TIMETZ_GT
#undef TIMETZ_LT
}


Datum timetz_time(PG_FUNCTION_ARGS)
{
	TimeTzADT  *fc_timetz = PG_GETARG_TIMETZADT_P(0);
	TimeADT		fc_result;

	/* 忽略时区并仅返回时间 */
	fc_result = fc_timetz->time;

	PG_RETURN_TIMEADT(fc_result);
}


Datum time_timetz(PG_FUNCTION_ARGS)
{
	TimeADT		fc_time = PG_GETARG_TIMEADT(0);
	TimeTzADT  *fc_result;
	struct pg_tm fc_tt,
			   *fc_tm = &fc_tt;
	fsec_t		fc_fsec;
	int			fc_tz;

	GetCurrentDateTime(fc_tm);
	time2tm(fc_time, fc_tm, &fc_fsec);
	fc_tz = DetermineTimeZoneOffset(fc_tm, session_timezone);

	fc_result = (TimeTzADT *) palloc(sizeof(TimeTzADT));

	fc_result->time = fc_time;
	fc_result->zone = fc_tz;

	PG_RETURN_TIMETZADT_P(fc_result);
}


/* timestamptz_timetz()
 * 将时间戳转换为 timetz 数据类型。
 */
Datum timestamptz_timetz(PG_FUNCTION_ARGS)
{
	TimestampTz fc_timestamp = PG_GETARG_TIMESTAMP(0);
	TimeTzADT  *fc_result;
	struct pg_tm fc_tt,
			   *fc_tm = &fc_tt;
	int			fc_tz;
	fsec_t		fc_fsec;

	if (TIMESTAMP_NOT_FINITE(fc_timestamp))
		PG_RETURN_NULL();

	if (timestamp2tm(fc_timestamp, &fc_tz, fc_tm, &fc_fsec, NULL, NULL) != 0)
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));

	fc_result = (TimeTzADT *) palloc(sizeof(TimeTzADT));

	tm2timetz(fc_tm, fc_fsec, fc_tz, fc_result);

	PG_RETURN_TIMETZADT_P(fc_result);
}


/* datetimetz_timestamptz()
 * 将日期和 时区时间 转换为带有时区的数据类型的时间戳。
 * 时间戳以 GMT 存储，因此将存储在时区时间中的时区
 * 添加到结果中。
 * - thomas 2000-03-10
 */
Datum datetimetz_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		fc_date = PG_GETARG_DATEADT(0);
	TimeTzADT  *fc_time = PG_GETARG_TIMETZADT_P(1);
	TimestampTz fc_result;

	if (DATE_IS_NOBEGIN(fc_date))
		TIMESTAMP_NOBEGIN(fc_result);
	else if (DATE_IS_NOEND(fc_date))
		TIMESTAMP_NOEND(fc_result);
	else
	{
		/*
		 * 日期的范围比时间戳的范围更广，因此检查边界。
		 * 由于日期的最小值与时间戳相同，因此只需检查上边界是否溢出。
		 */
		if (fc_date >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("date out of range for timestamp")));
		fc_result = fc_date * USECS_PER_DAY + fc_time->time + fc_time->zone * USECS_PER_SEC;

		/*
		 * 由于可能因为时区而超出允许的 timestamptz 范围，
		 * 因此在添加时区后检查允许的时间戳范围。
		 */
		if (!IS_VALID_TIMESTAMP(fc_result))
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("date out of range for timestamp")));
	}

	PG_RETURN_TIMESTAMP(fc_result);
}


/* timetz_part() 和 extract_timetz()
 * 从时间类型中提取指定字段。
 */
static Datum fc_timetz_part_common(PG_FUNCTION_ARGS, bool fc_retnumeric)
{
	text	   *fc_units = PG_GETARG_TEXT_PP(0);
	TimeTzADT  *fc_time = PG_GETARG_TIMETZADT_P(1);
	int64		fc_intresult;
	int			fc_type,
				fc_val;
	char	   *fc_lowunits;

	fc_lowunits = downcase_truncate_identifier(VARDATA_ANY(fc_units),
											VARSIZE_ANY_EXHDR(fc_units),
											false);

	fc_type = DecodeUnits(0, fc_lowunits, &fc_val);
	if (fc_type == UNKNOWN_FIELD)
		fc_type = DecodeSpecial(0, fc_lowunits, &fc_val);

	if (fc_type == UNITS)
	{
		int			fc_tz;
		fsec_t		fc_fsec;
		struct pg_tm fc_tt,
				   *fc_tm = &fc_tt;

		timetz2tm(fc_time, fc_tm, &fc_fsec, &fc_tz);

		switch (fc_val)
		{
			case DTK_TZ:
				fc_intresult = -fc_tz;
				break;

			case DTK_TZ_MINUTE:
				fc_intresult = (-fc_tz / SECS_PER_MINUTE) % MINS_PER_HOUR;
				break;

			case DTK_TZ_HOUR:
				fc_intresult = -fc_tz / SECS_PER_HOUR;
				break;

			case DTK_MICROSEC:
				fc_intresult = fc_tm->tm_sec * INT64CONST(1000000) + fc_fsec;
				break;

			case DTK_MILLISEC:
				if (fc_retnumeric)
					/*---
					 * tm->tm_sec * 1000 + fsec / 1000
					 * = (tm->tm_sec * 1'000'000 + fsec) / 1000
					 */
					PG_RETURN_NUMERIC(int64_div_fast_to_numeric(fc_tm->tm_sec * INT64CONST(1000000) + fc_fsec, 3));
				else
					PG_RETURN_FLOAT8(fc_tm->tm_sec * 1000.0 + fc_fsec / 1000.0);
				break;

			case DTK_SECOND:
				if (fc_retnumeric)
					/*---
					 * tm->tm_sec + fsec / 1'000'000
					 * = (tm->tm_sec * 1'000'000 + fsec) / 1'000'000
					 */
					PG_RETURN_NUMERIC(int64_div_fast_to_numeric(fc_tm->tm_sec * INT64CONST(1000000) + fc_fsec, 6));
				else
					PG_RETURN_FLOAT8(fc_tm->tm_sec + fc_fsec / 1000000.0);
				break;

			case DTK_MINUTE:
				fc_intresult = fc_tm->tm_min;
				break;

			case DTK_HOUR:
				fc_intresult = fc_tm->tm_hour;
				break;

			case DTK_DAY:
			case DTK_MONTH:
			case DTK_QUARTER:
			case DTK_YEAR:
			case DTK_DECADE:
			case DTK_CENTURY:
			case DTK_MILLENNIUM:
			default:
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("unit \"%s\" not supported for type %s",
								fc_lowunits, format_type_be(TIMETZOID))));
				fc_intresult = 0;
		}
	}
	else if (fc_type == RESERV && fc_val == DTK_EPOCH)
	{
		if (fc_retnumeric)
			/*---
			 * time->time / 1'000'000 + time->zone
			 * = (time->time + time->zone * 1'000'000) / 1'000'000
			 */
			PG_RETURN_NUMERIC(int64_div_fast_to_numeric(fc_time->time + fc_time->zone * INT64CONST(1000000), 6));
		else
			PG_RETURN_FLOAT8(fc_time->time / 1000000.0 + fc_time->zone);
	}
	else
	{
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("unit \"%s\" not recognized for type %s",
						fc_lowunits, format_type_be(TIMETZOID))));
		fc_intresult = 0;
	}

	if (fc_retnumeric)
		PG_RETURN_NUMERIC(int64_to_numeric(fc_intresult));
	else
		PG_RETURN_FLOAT8(fc_intresult);
}


Datum timetz_part(PG_FUNCTION_ARGS)
{
	return fc_timetz_part_common(fcinfo, false);
}

Datum extract_timetz(PG_FUNCTION_ARGS)
{
	return fc_timetz_part_common(fcinfo, true);
}

/* timetz_zone()
 * 使用指定时区对带时区的时间进行编码。
 * 根据事务开始时的时间应用夏令时规则。
 */
Datum timetz_zone(PG_FUNCTION_ARGS)
{
	text	   *fc_zone = PG_GETARG_TEXT_PP(0);
	TimeTzADT  *fc_t = PG_GETARG_TIMETZADT_P(1);
	TimeTzADT  *fc_result;
	int			fc_tz;
	char		fc_tzname[TZ_STRLEN_MAX + 1];
	char	   *fc_lowzone;
	int			fc_type,
				fc_val;
	pg_tz	   *fc_tzp;

	/*
	 * 查找请求的时区。首先，我们查看时区缩写表（处理类似“EST”的情况），如果失败，则查看时区数据库（处理类似“America/New_York”的情况）。（这与时间戳输入检查情况的顺序相匹配；这很重要，因为时区数据库不明智地使用了几个与偏移缩写相同的区域名称。）
	 */
	text_to_cstring_buffer(fc_zone, fc_tzname, sizeof(fc_tzname));

	/* DecodeTimezoneAbbrev需要小写输入 */
	fc_lowzone = downcase_truncate_identifier(fc_tzname,
										   strlen(fc_tzname),
										   false);

	fc_type = DecodeTimezoneAbbrev(0, fc_lowzone, &fc_val, &fc_tzp);

	if (fc_type == TZ || fc_type == DTZ)
	{
		/* 固定偏移缩写 */
		fc_tz = -fc_val;
	}
	else if (fc_type == DYNTZ)
	{
		/* 动态偏移缩写，使用事务开始时间解析 */
		TimestampTz fc_now = GetCurrentTransactionStartTimestamp();
		int			fc_isdst;

		fc_tz = DetermineTimeZoneAbbrevOffsetTS(fc_now, fc_tzname, fc_tzp, &fc_isdst);
	}
	else
	{
		/* 尝试将其作为完整区域名称 */
		fc_tzp = pg_tzset(fc_tzname);
		if (fc_tzp)
		{
			/* 获取当前区域的有效 GMT 偏移 */
			TimestampTz fc_now = GetCurrentTransactionStartTimestamp();
			struct pg_tm fc_tm;
			fsec_t		fc_fsec;

			if (timestamp2tm(fc_now, &fc_tz, &fc_tm, &fc_fsec, NULL, fc_tzp) != 0)
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
		}
		else
		{
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("time zone \"%s\" not recognized", fc_tzname)));
			fc_tz = 0;				/* 保持编译器安静 */
		}
	}

	fc_result = (TimeTzADT *) palloc(sizeof(TimeTzADT));

	fc_result->time = fc_t->time + (fc_t->zone - fc_tz) * USECS_PER_SEC;
	/* C99 模模操作对于负输入有错误的符号约定 */
	while (fc_result->time < INT64CONST(0))
		fc_result->time += USECS_PER_DAY;
	if (fc_result->time >= USECS_PER_DAY)
		fc_result->time %= USECS_PER_DAY;

	fc_result->zone = fc_tz;

	PG_RETURN_TIMETZADT_P(fc_result);
}

/* timetz_izone()
 * 使用指定时间间隔作为时区编码带时区类型的时间。
 */
Datum timetz_izone(PG_FUNCTION_ARGS)
{
	Interval   *fc_zone = PG_GETARG_INTERVAL_P(0);
	TimeTzADT  *fc_time = PG_GETARG_TIMETZADT_P(1);
	TimeTzADT  *fc_result;
	int			fc_tz;

	if (fc_zone->month != 0 || fc_zone->day != 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("interval time zone \"%s\" must not include months or days",
						DatumGetCString(DirectFunctionCall1(interval_out,
															PointerGetDatum(fc_zone))))));

	fc_tz = -(fc_zone->time / USECS_PER_SEC);

	fc_result = (TimeTzADT *) palloc(sizeof(TimeTzADT));

	fc_result->time = fc_time->time + (fc_time->zone - fc_tz) * USECS_PER_SEC;
	/* C99 模模操作对于负输入有错误的符号约定 */
	while (fc_result->time < INT64CONST(0))
		fc_result->time += USECS_PER_DAY;
	if (fc_result->time >= USECS_PER_DAY)
		fc_result->time %= USECS_PER_DAY;

	fc_result->zone = fc_tz;

	PG_RETURN_TIMETZADT_P(fc_result);
}
